﻿using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Design;
using System.Collections;
using EnvDTE;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Globalization;

namespace Adaptive.Foundation.CodeDom.Native
{
    public class VsCodeDomProvider : CodeDomProvider, ICodeDomDesignerReload
    {
        // Fields
        private bool addedExtender = false;
        private ICodeGenerator codeGenerator;
        private CodeModel codeModel;
        private ICodeParser codeParser;
        private FileCodeModel fileCodeModel;
        private Hashtable fullParseHandlers;
        private const string GeneratedCodeKey = "_Method_Generated_Code";
        private IDictionary generatedFields;
        private IDictionary generatedMethods;
        private CodeDomProvider generatorProvider;
        private short isVB;
        private const short ISVB_NO = 2;
        private const short ISVB_NOTCHECKED = 0;
        private const short ISVB_YES = 1;
        private int itemid;
        private ProjectItem projectItem;
        private IServiceProvider serviceProvider;
        internal static BooleanSwitch vbBatchGen = new BooleanSwitch("VbBatchGen", "turn on Visual Basic Batch");
        private IVsHierarchy vsHierarchy;

        // Methods
        public VsCodeDomProvider(IServiceProvider serviceProvider, ProjectItem projectItem, CodeDomProvider generatorProvider)
        {
            this.projectItem = projectItem;
            this.serviceProvider = serviceProvider;
            this.generatorProvider = generatorProvider;
        }

        public void AddGeneratedField(CodeTypeDeclaration codeType, CodeMemberField field)
        {
            this.GeneratedFields[codeType.Name + ':' + field.Name] = field;
        }

        public void AddGeneratedMethod(CodeTypeDeclaration codeType, CodeMemberMethod method)
        {
            this.AddGeneratedMethod(codeType, method, null);
        }

        public void AddGeneratedMethod(CodeTypeDeclaration codeType, CodeMemberMethod method, string code)
        {
            CodeElement element = method.UserData[VsCodeDomParser.VsElementKey] as CodeElement;
            if (element != null)
            {
                if (code == null)
                {
                    code = element.StartPoint.CreateEditPoint().GetText(element.EndPoint);
                }
                this.GeneratedMethods[string.Concat(new object[] { codeType.Name, ':', method.Name, ':', method.Parameters.Count.ToString() })] = method;
                method.UserData["_Method_Generated_Code"] = code;
            }
        }

        public void ClearGeneratedMembers()
        {
            if (this.generatedMethods != null)
            {
                this.generatedMethods.Clear();
            }
            if (this.generatedFields != null)
            {
                this.generatedFields.Clear();
            }
        }
        [Obsolete()]
        public override ICodeCompiler CreateCompiler()
        {
            return null;
        }
        [Obsolete()]
        public override ICodeGenerator CreateGenerator()
        {
            return this.CodeGenerator;
        }
        [Obsolete()]
        public override ICodeParser CreateParser()
        {
            return this.CodeParser;
        }

        public void EndEdit()
        {
            IVBFileCodeModelEvents fileCodeModel = this.FileCodeModel as IVBFileCodeModelEvents;
            if (fileCodeModel != null)
            {
                fileCodeModel.EndEdit();
            }
        }

        public void EnsureExtender(IServiceProvider sp)
        {
            if (!this.addedExtender && (sp != null))
            {
                IExtenderProviderService service = (IExtenderProviderService)sp.GetService(typeof(IExtenderProviderService));
                if (service != null)
                {
                    service.AddExtenderProvider(new RootComponentNameProvider(sp, (this.LanguageOptions & LanguageOptions.CaseInsensitive) != LanguageOptions.None, this.CodeGenerator));
                    this.addedExtender = true;
                }
            }
        }

        private CodeTypeMember FindMatchingMember(CodeCompileUnit compileUnit, string className, CodeTypeMember searchMember, out CodeTypeDeclaration codeTypeDecl)
        {
            foreach (System.CodeDom.CodeNamespace namespace2 in compileUnit.Namespaces)
            {
                foreach (CodeTypeDeclaration declaration in namespace2.Types)
                {
                    if (((declaration.Attributes & MemberAttributes.Public) == ((MemberAttributes)0)) || !(declaration.Name == className))
                    {
                        continue;
                    }
                    foreach (CodeTypeMember member in declaration.Members)
                    {
                        if (((member.Attributes != searchMember.Attributes) || !(member.Name == searchMember.Name)) || !searchMember.GetType().IsAssignableFrom(member.GetType()))
                        {
                            continue;
                        }
                        if (searchMember is CodeMemberMethod)
                        {
                            CodeMemberMethod method = member as CodeMemberMethod;
                            CodeMemberMethod method2 = searchMember as CodeMemberMethod;
                            if (method.Parameters.Count != method2.Parameters.Count)
                            {
                                continue;
                            }
                            bool flag = true;
                            for (int i = 0; i < method2.Parameters.Count; i++)
                            {
                                if (method2.Parameters[i].Type.BaseType != method.Parameters[i].Type.BaseType)
                                {
                                    flag = false;
                                    break;
                                }
                            }
                            if (!flag)
                            {
                                continue;
                            }
                            codeTypeDecl = declaration;
                            return method;
                        }
                        if (searchMember is CodeMemberField)
                        {
                            CodeMemberField field = member as CodeMemberField;
                            CodeMemberField field2 = searchMember as CodeMemberField;
                            if (!(field.Type.BaseType == field2.Type.BaseType))
                            {
                                continue;
                            }
                            codeTypeDecl = declaration;
                            return field;
                        }
                    }
                }
            }
            codeTypeDecl = null;
            return null;
        }

        public override TypeConverter GetConverter(Type type)
        {
            if (this.generatorProvider != null)
            {
                return this.generatorProvider.GetConverter(type);
            }
            return TypeDescriptor.GetConverter(type);
        }

        public void StartEdit()
        {
            IVBFileCodeModelEvents fileCodeModel = this.FileCodeModel as IVBFileCodeModelEvents;
            if (fileCodeModel != null)
            {
                fileCodeModel.StartEdit();
            }
        }

        bool ICodeDomDesignerReload.ShouldReloadDesigner(CodeCompileUnit newTree)
        {
            if (((this.generatedMethods == null) || (this.generatedMethods.Count == 0)) && ((this.generatedFields == null) || (this.generatedFields.Count == 0)))
            {
                return true;
            }
            Hashtable hashtable = null;
            char[] separator = new char[] { ':' };
            CodeTypeDeclaration codeTypeDecl = null;
            if (this.generatedFields != null)
            {
                foreach (DictionaryEntry entry in this.generatedFields)
                {
                    string key = (string)entry.Key;
                    CodeMemberField searchMember = entry.Value as CodeMemberField;
                    if (searchMember != null)
                    {
                        string[] strArray = key.Split(separator);
                        if (!(this.FindMatchingMember(newTree, strArray[0], searchMember, out codeTypeDecl) is CodeMemberField))
                        {
                            return true;
                        }
                    }
                }
            }
            if (this.generatedMethods != null)
            {
                foreach (DictionaryEntry entry2 in this.generatedMethods)
                {
                    string str2 = (string)entry2.Key;
                    CodeMemberMethod method = entry2.Value as CodeMemberMethod;
                    if (method != null)
                    {
                        string[] strArray2 = str2.Split(separator);
                        CodeMemberMethod method2 = this.FindMatchingMember(newTree, strArray2[0], method, out codeTypeDecl) as CodeMemberMethod;
                        if (method2 == null)
                        {
                            return true;
                        }
                        CodeElement element = method2.UserData[VsCodeDomParser.VsElementKey] as CodeElement;
                        if (element == null)
                        {
                            return true;
                        }
                        string text = element.StartPoint.CreateEditPoint().GetText(element.EndPoint);
                        string str4 = method.UserData["_Method_Generated_Code"] as string;
                        if (str4 != text)
                        {
                            return true;
                        }
                        DictionaryEntry item = entry2;

                        item.Value = method2;
                        method2.UserData["_Method_Generated_Code"] = str4;
                        method2.UserData[VsCodeDomParser.VsGenerateStatementsKey] = VsCodeDomParser.VsGenerateStatementsKey;
                        if (hashtable == null)
                        {
                            hashtable = (Hashtable)codeTypeDecl.UserData[VsCodeDomParser.VbHandlesClausesKey];
                        }
                    }
                }
            }
            if (this.IsVB)
            {
                if ((this.fullParseHandlers == null) != (hashtable == null))
                {
                    return true;
                }
                if (hashtable != null)
                {
                    string[] array = new string[hashtable.Keys.Count];
                    hashtable.Keys.CopyTo(array, 0);
                    string[] strArray4 = new string[this.fullParseHandlers.Count];
                    this.fullParseHandlers.Keys.CopyTo(strArray4, 0);
                    Array.Sort(array, InvariantComparer.Default);
                    Array.Sort(strArray4, InvariantComparer.Default);
                    if (strArray4.Length != array.Length)
                    {
                        return true;
                    }
                    for (int i = 0; i < array.Length; i++)
                    {
                        if (!array[i].Equals(strArray4[i]))
                        {
                            return true;
                        }
                    }
                    foreach (DictionaryEntry entry4 in this.fullParseHandlers)
                    {
                        CodeMemberMethod method3 = hashtable[entry4.Key] as CodeMemberMethod;
                        if (method3 != null)
                        {
                            CodeMemberMethod method4 = entry4.Value as CodeMemberMethod;
                            if ((method3.Name != method4.Name) || (method3.ReturnType.BaseType != method4.ReturnType.BaseType))
                            {
                                return true;
                            }
                            if (method3.Parameters.Count == method4.Parameters.Count)
                            {
                                for (int j = 0; j < method3.Parameters.Count; j++)
                                {
                                    if (method3.Parameters[j].Type.BaseType != method3.Parameters[j].Type.BaseType)
                                    {
                                        return true;
                                    }
                                }
                                continue;
                            }
                        }
                        return true;
                    }
                }
            }
            return false;
        }

        // Properties
        internal ICodeGenerator CodeGenerator
        {
            get
            {
                if ((this.codeGenerator == null) && (this.generatorProvider != null))
                {
                    this.codeGenerator = new VsCodeDomGenerator(this.generatorProvider.CreateGenerator(), this);
                }
                return this.codeGenerator;
            }
        }

        public CodeModel CodeModel
        {
            get
            {
                if ((this.codeModel == null) && (this.FileCodeModel != null))
                {
                    ProjectItem item = this.FileCodeModel.Parent;
                    if (item != null)
                    {
                        Project project = item.ContainingProject;
                        if (project != null)
                        {
                            this.codeModel = project.CodeModel;
                        }
                    }
                }
                return this.codeModel;
            }
        }

        internal ICodeParser CodeParser
        {
            get
            {
                if ((this.codeParser == null) && (this.FileCodeModel != null))
                {
                    this.codeParser = new VsCodeDomParser(this);
                }
                return this.codeParser;
            }
        }

        public System.CodeDom.CodeNamespace DefaultNamespace
        {
            get
            {
                object obj2;
                if ((this.Hierarchy != null) && ((this.vsHierarchy.GetProperty(this.itemid, -2049, out obj2)) > 0))
                {
                    return new System.CodeDom.CodeNamespace(Convert.ToString(obj2));
                }
                return new System.CodeDom.CodeNamespace("");
            }
        }

        public FileCodeModel FileCodeModel
        {
            get
            {
                if ((this.fileCodeModel == null) && (this.projectItem != null))
                {
                    this.fileCodeModel = this.projectItem.FileCodeModel;
                }
                
                return this.fileCodeModel;
            }
        }

        public override string FileExtension
        {
            get
            {
                string fileExtension = this.generatorProvider.FileExtension;
                if (fileExtension != null)
                {
                    return fileExtension;
                }
                return base.FileExtension;
            }
        }

        public string FileName
        {
            get
            {
                return this.projectItem.get_FileNames(1);
            }
        }

        internal Hashtable FullParseHandlers
        {
            get
            {
                return this.fullParseHandlers;
            }
            set
            {
                this.fullParseHandlers = value;
            }
        }

        private IDictionary GeneratedFields
        {
            get
            {
                if (this.generatedFields == null)
                {
                    this.generatedFields = new Hashtable();
                }
                return this.generatedFields;
            }
        }

        private IDictionary GeneratedMethods
        {
            get
            {
                if (this.generatedMethods == null)
                {
                    this.generatedMethods = new Hashtable();
                }
                return this.generatedMethods;
            }
        }

        public IVsHierarchy Hierarchy
        {
            get
            {
                if (this.vsHierarchy == null)
                {
                    this.vsHierarchy = ShellDocumentManager.GetHierarchyForFile(this.serviceProvider, this.FileName, out this.itemid);
                }
                return this.vsHierarchy;
            }
        }

        public bool IsVB
        {
            get
            {
                if (this.isVB == 0)
                {
                    this.isVB = 2;
                    if ((this.CodeModel != null) && (string.Compare(this.CodeModel.Language, "{B5E9BD33-6D3E-4B5D-925E-8A43B79820B4}", true, CultureInfo.InvariantCulture) == 0))
                    {
                        this.isVB = 1;
                    }
                }
                return (this.isVB == 1);
            }
        }

        public override LanguageOptions LanguageOptions
        {
            get
            {
                return this.generatorProvider.LanguageOptions;
            }
        }

        public ITypeResolutionService TypeLoader
        {
            get
            {
                ITypeResolutionServiceProvider service = (ITypeResolutionServiceProvider)this.serviceProvider.GetService(typeof(ITypeResolutionServiceProvider));
                if (service != null)
                {
                    return service.GetTypeResolutionService(this.Hierarchy);
                }
                return null;
            }
        }

        // Nested Types
        [ProvideProperty("Name", typeof(IComponent))]
        private class RootComponentNameProvider : System.ComponentModel.IExtenderProvider
        {
            // Fields
            private ICodeGenerator codeGenerator;
            private IDesignerHost host;
            private bool ignoreCase;
            private bool inSetName;
            private bool nameFail;
            private IServiceProvider sp;

            // Methods
            public RootComponentNameProvider(IServiceProvider sp, bool ignoreCase, ICodeGenerator gen)
            {
                this.sp = sp;
                this.host = (IDesignerHost)sp.GetService(typeof(IDesignerHost));
                this.codeGenerator = gen;
                this.ignoreCase = ignoreCase;
            }

            [DesignOnly(true), ParenthesizePropertyName(true), Category("Design")]
            public string GetName(IComponent comp)
            {
                if (!this.nameFail)
                {
                    try
                    {
                        CodeClass documentCodeClass = this.DocumentCodeClass;
                        if (documentCodeClass != null)
                        {
                            return documentCodeClass.Name;
                        }
                    }
                    catch
                    {
                        this.nameFail = true;
                        TypeDescriptor.Refresh(comp);
                    }
                }
                return "";
            }

            public void SetName(IComponent comp, string value)
            {
                if (!this.inSetName && !this.host.Loading)
                {
                    this.inSetName = true;
                    try
                    {
                        CodeClass documentCodeClass = this.DocumentCodeClass;
                        if ((documentCodeClass != null) && (string.Compare(documentCodeClass.Name, value, this.ignoreCase, CultureInfo.InvariantCulture) != 0))
                        {
                            this.codeGenerator.ValidateIdentifier(value);
                            try
                            {
                                this.codeGenerator.ValidateIdentifier(value + "Handler");
                            }
                            catch
                            {
                                throw new ArgumentException("SerializerInvalidIdentifier");
                            }
                            if (this.host.Container.Components[value] != null)
                            {
                                Exception exception = new Exception("CODEMANDupComponentName");
                                exception.HelpLink = "CODEMANDupComponentName";
                                throw exception;
                            }
                            documentCodeClass.Name = value;
                            CodeTypeDeclaration documentCodeType = this.DocumentCodeType;
                            if (documentCodeType != null)
                            {
                                documentCodeType.Name = value;
                            }
                            if (comp.Site != null)
                            {
                                comp.Site.Name = value;
                            }
                        }
                    }
                    finally
                    {
                        this.inSetName = false;
                    }
                }
            }

            bool System.ComponentModel.IExtenderProvider.CanExtend(object extendee)
            {
                return (!this.nameFail && (extendee == this.host.RootComponent));
            }

            // Properties
            private CodeClass DocumentCodeClass
            {
                get
                {
                    CodeTypeDeclaration documentCodeType = this.DocumentCodeType;
                    if (documentCodeType != null)
                    {
                        return (documentCodeType.UserData[VsCodeDomParser.VsElementKey] as CodeClass);
                    }
                    return null;
                }
            }

            private CodeTypeDeclaration DocumentCodeType
            {
                get
                {
                    return (CodeTypeDeclaration)this.host.GetService(typeof(CodeTypeDeclaration));
                }
            }
        }
    }
}
